/****
 *
 * $Id: main.c,v 1.22 2009/09/30 01:19:39 rdilley Exp $
 * $Name:  $
 *
 * Copyright (c) 2009, Ron Dilley
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   - Neither the name of Uberadmin/BaraCUDA/Nightingale nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ****/

/****
 *
 * includes
 *
 ****/

#include "baracuda.h"

/****
 *
 * defines
 *
 ****/

/****
 *
 * local variables
 *
 ****/

PRIVATE char *cvsid = "$Id: main.c,v 1.22 2009/09/30 01:19:39 rdilley Exp $";

/****
 *
 * global variables
 *
 ****/

/* metrics */
unsigned long long totalTries = 0;
unsigned long long lastTotalTries = 0;
unsigned long long totalGuessed = 0;
unsigned long long lastTotalGuessed = 0;

/* signal flags */
static volatile sig_atomic_t caughtAlarmSignal = FALSE;
static volatile quit = FALSE;
static volatile reload = FALSE;
static volatile save = TRUE;

int haveCuda = FALSE;
int threadCount = 1;
int debug = 0;
char *potFileName = NULL;
char *sessionFileName = NULL;
int passwordLen = MAX_PASSWORD_LEN;
int testIt = FALSE;

/* time related globals, don't update time too frequently */
time_t startTime = 0;
time_t currentTime = 0;
time_t lastTime = 0;

/* pthreads */
threadWorkUnit_t threadWorkUnits[MAX_COMPARE_THREADS];
pthread_mutex_t mutexWorkUnits;

/****
 *
 * external global variables
 *
 ****/

extern struct hash_s *uhHash;
extern pureBruteForceState_t pbfState;

/****
 *
 * external functions
 *
 ****/

/****
 *
 * main function
 *
 ****/

int main( int argc, char *argv[] ) {
  int deviceCount = 0, c = 0, digit_optind = 0;

  while (1) {
    int this_option_optind = optind ? optind : 1;
#ifdef HAVE_GETOPT_LONG
    int option_index = 0;
    static struct option long_options[] = {
      {"version", no_argument, 0, 'v' },
      {"debug", required_argument, 0, 'd' },
      {"help", no_argument, 0, 'h' },
      {"known", required_argument, 0, 'k' },
      {"session", required_argument, 0, 's' },
      {"threads", required_argument, 0, 't' },
      {"pwlen", required_argument, 0, 'l' },
      {"test", no_argument, 0, 'x' },
      {0, no_argument, 0, 0}
    };
    c = getopt_long(argc, argv, "vd:hk:s:t:l:x", long_options, &option_index);
#else
    c = getopt( argc, argv, "vd:hk:s:t:l:x" );
#endif

    if (c EQ -1)
      break;

    switch (c) {

    case 'v':
      /* show the version */
      print_version();
      return( EXIT_SUCCESS );

    case 'd':
      /* show debig info */
      debug = atoi( optarg );
      break;

    case 'h':
      /* show help info */
      print_help( argv[0] );
      return( EXIT_SUCCESS );

    case 's':
      if ( ( sessionFileName = XMALLOC( strlen( optarg ) ) ) EQ NULL )
	die( "Unable to allocate memory for session file name\n" );
      XSTRCPY( sessionFileName, optarg );
      loadState( sessionFileName );
      break;

    case 't':
      /* over ride thread count */
      if ( ( threadCount = atoi( optarg ) ) > MAX_COMPARE_THREADS )
	threadCount = MAX_COMPARE_THREADS;
      break;

    case 'k':
      if ( ( potFileName = XMALLOC( strlen( optarg ) ) ) EQ NULL )
	die( "Unable to allocate memory for pot file name\n" );
      XSTRCPY( potFileName, optarg );
      break;

    case 'l':
      if ( ( passwordLen = atoi( optarg ) ) > MAX_PASSWORD_LEN )
	passwordLen = MAX_PASSWORD_LEN;
      break;

    case 'x':
      testIt = TRUE;
      break;

    default:
      fprintf( stderr, "Unknown option code [0%o]\n", c);
    }
  }

  /* set defaults if not over-ridden */
  if ( potFileName EQ NULL )
    potFileName = "baracuda.pot";

  if ( sessionFileName EQ NULL )
    sessionFileName = "baracuda.session";

  /* brag */
  print_version();

  /* load target hash list */
  if ( optind EQ argc )
    die("Syntax: %s [option]... hashfile [hashfile]...\n", argv[0] );

  /* setup gracefull shutdown */
  signal( SIGINT, sigint_handler );
  signal( SIGTERM, sigterm_handler );
  signal( SIGFPE, sigfpe_handler );
  signal( SIGBUS, sigbus_handler );
  signal( SIGILL, sigill_handler );
  signal( SIGHUP, sighup_handler );
  signal( SIGSEGV, sigsegv_handler );

  while( optind < argc ) {
    if ( ! loadUnknownHashes( argv[optind++] ) )
      return (EXIT_FAILURE);
  }
  /* load known hash pot file */
  loadKnownHashes( potFileName );

  /* init cuda if we can */
  if ( ! cudaInit() ) {
    die("Unable to start CUDA\n" );
  } else
    haveCuda = TRUE;

  /****
   *
   * get to work
   *
   ****/

  getCracking();

  /****
   *
   * clean up
   *
   ****/

  freeHash( uhHash );

  /* shuwdown cuda */
  cudaExit();

  /* done, bail */
  return( EXIT_SUCCESS );
}

/*****
 *
 * interrupt handler (current time)
 *
 *****/

void crackSpeed( int signo ) {
  /* disable SIGALRM */
  signal( SIGALRM, SIG_IGN );

  caughtAlarmSignal = TRUE;

  /* reset SIGALRM */
  signal( SIGALRM, crackSpeed );
  /* reset alarm */
  alarm( METRICS_INTERVAL );
}

/****
 *
 * all the work is done here
 *
 ****/

int getCracking( void ) {
  unsigned int gpuFreeMem;
  int gpuCores;
  int workUnitsPerCore;
  int mapMemSize;
  workUnit_t *workUnitBuf, *goldWorkUnitBuf;
  int i, count, ret;
  long threadId;
  unsigned long long triesThisPeriod = 0, guessedThisPeriod = 0;
  pthread_t threads[MAX_COMPARE_THREADS];

  /* init thread buffers */
  for( i = 0; i < MAX_COMPARE_THREADS; i++ ) {
    threadWorkUnits[i].workUnits = NULL;
  }

  if ( haveCuda ) {
    gpuFreeMem = getCudaFreeMem( 0 );
    gpuCores = getCudaCores( 0 );

    workUnitsPerCore = ( ( gpuFreeMem / sizeof( workUnit_t ) ) / gpuCores ) / 4;
    mapMemSize = workUnitsPerCore * gpuCores * sizeof( workUnit_t );
    if ( debug >= 1 ) {
      printf( "%d cores with %d free memory\n", gpuCores, gpuFreeMem );
      printf( "%d work units per core\n", workUnitsPerCore );
      printf( "%d memory allocated for cpu<->gpu comms\n", mapMemSize );
    }
  } else {
    gpuFreeMem = 4096000;
    gpuCores = 16;
    workUnitsPerCore = ( ( gpuFreeMem / sizeof( workUnit_t ) ) / gpuCores ) / 4;
    mapMemSize = workUnitsPerCore * gpuCores * sizeof( workUnit_t );
  }

  if ( debug >= 1 ) {
    fprintf( stderr, "Running (%d) compare threads\n", threadCount );
    fprintf( stderr, "Maximum password length is (%d)\n", passwordLen );
  }

  /****
   *
   * GET TO WORK
   *
   ****/

  printf( "I pwn, therefore I am . . .\n" );

  /* start the clock */
  currentTime = lastTime = startTime = time( NULL );

  /* reset SIGALRM */
  signal( SIGALRM, crackSpeed );
  /* reset alarm */
  alarm( METRICS_INTERVAL );

  /****
   *
   * dictionary crack
   *
   ****/

  /****
   *
   * probabilistic context-free grammars
   *
   ****/

  /****
   *
   * markov models
   *
   ****/

  /****
   *
   * pure brute force the short passwords
   *
   ****/

  /* allocate memory for new work unit */
  if ( ( workUnitBuf = (workUnit_t *)workUnitAlloc( workUnitsPerCore * gpuCores * sizeof( workUnit_t ) ) ) EQ NULL ) {
    die("ERR - Unable to allocate memory for work units\n" );
  }

  do {
  
    /****
     *
     * load chunk of plaintext
     *
     ****/

    /* XXX this may benifit from working in parallel for complex plaintext builders */
    count = loadBruteForceWorkUnits( workUnitBuf, workUnitsPerCore * gpuCores, passwordLen );
#ifdef DEBUG
    if ( debug >= 5 )
      fprintf( stderr, "%d work units loaded\n", count );
#endif

    /****
     *
     * generate hashes
     *
     ****/

    /* XXX if CUDA does not exist, use local CPU */
    if ( haveCuda )
      count = gpuProcessWorkUnits( workUnitBuf, count, uhHash->size );
    else
      count = processWorkUnits( workUnitBuf, count, uhHash->size );

    /* test it */
    if ( testIt ) {
      /* make room in host memory for a test work unit block */
      if ( ( goldWorkUnitBuf = XMALLOC( workUnitsPerCore * gpuCores * sizeof( workUnit_t ) ) ) EQ NULL ) {
	die( "ERR - Unable to allocate memory to test work units\n" );
      }
      /* copy processed work unit block from page locked memory to std host memory */
      memcpy( goldWorkUnitBuf, workUnitBuf, workUnitsPerCore * gpuCores * sizeof( workUnit_t ) );
    }

    /****
     *
     * look work units up
     *
     ****/

    /* XXX this needs to be a separate thread, 70% of the time is spent here */

    /* create threats to search for matches */
    for( threadId = 0; threadId < threadCount; threadId++ ) {
#ifdef DEBUG
      fprintf( stderr,
	       "p> tid: %d t: %lu o: %lu c: %lu\n",
	       threadId, count, (count/threadCount)*threadId,
	       count/threadCount
	       );
#endif
      threadWorkUnits[threadId].workUnits = &workUnitBuf[(count/threadCount)*threadId];
      threadWorkUnits[threadId].count = 0;
      threadWorkUnits[threadId].workUnitCount = count/threadCount;
      threadWorkUnits[threadId].uhHash = uhHash;
      if ( ( ret = pthread_create( &threads[threadId], NULL, (void*)&threadCompareWorkUnits, (void *)threadId ) ) != 0 ) {
	die("ERR - Unable to start compare thread[%d] err(%d)\n", threadId, ret ); 
      }
    }
    /* wait for threats to finish */
    for( threadId = 0; threadId < threadCount; threadId++ ) {
      /* just say no to zombie threads, aka wait for the threads to finish */
      pthread_join( threads[threadId], NULL );
    }

    /* test */
    if ( testIt ) {
      processWorkUnits( goldWorkUnitBuf, count, uhHash->size );
      if ( memcmp( goldWorkUnitBuf, workUnitBuf, workUnitsPerCore * gpuCores * sizeof( workUnit_t ) ) != 0 )
	warn( "FAIL - work units do not match gold standard\n" );
      else
	fprintf( stderr, "SUCCESS - work units match gold standard\n" );

      XFREE( goldWorkUnitBuf );
    }

    /****
     *
     * done, loop it
     *
     ****/

    /* if we caught an alarm, report metrics */
    if ( caughtAlarmSignal ) {
      /* reset semaphore */
      caughtAlarmSignal = 0;
      /* limit number of calls to time */
      currentTime = time( NULL );

      /* update tries */
      triesThisPeriod = totalTries - lastTotalTries;
      guessedThisPeriod = totalGuessed - lastTotalGuessed;
      lastTotalTries = totalTries;
      lastTotalGuessed = totalGuessed;
      printf( "%llu total tries, %llu total guesses, %llu tries per second, %llu guesses per second\n",
	      totalTries,
	      totalGuessed,
	      triesThisPeriod / ( currentTime - lastTime ),
	      guessedThisPeriod / ( currentTime - lastTime )
	      );

      /* save time */
      lastTime = currentTime;
    }

  } while( count EQ ( workUnitsPerCore * gpuCores ) & ! quit );

  if ( save ) {
    /* save the current state so we can continue later */
    saveState( sessionFileName );
  }

  /****
   *
   * DONE
   *
   ****/

  /* free host page locked and device buffers */
  workUnitFree( workUnitBuf );

  return TRUE;
}

/*****
 *
 * display version info
 *
 *****/

PRIVATE void print_version( void ) {
  fprintf( stderr, "%s v%s [%s - %s]\n", PROGNAME, VERSION, __DATE__, __TIME__ );
}

/*****
 *
 * print help info
 *
 *****/

PRIVATE void print_help( char *progName ) {
  print_version();

  fprintf( stderr, "\n" );
  fprintf( stderr, "syntax: %s [option]... hashfile [hashfile]...\n", progName );

#ifdef HAVE_GETOPT_LONG
  fprintf( stderr, " -d|--debug (0-9)     enable debugging info\n" );
  fprintf( stderr, " -h|--help            this info\n" );
  fprintf( stderr, " -k|--known {file}    specify a pot file (default: baracuda.pot)\n" );
  fprintf( stderr, " -s|--session {file}  specify a previous session to continue\n" );
  fprintf( stderr, " -t|--threads {num}   specify number of threads (max: %d)\n", MAX_COMPARE_THREADS );
  fprintf( stderr, " -v|--version         display version information\n" );
  fpritnf( stderr, " -x|--test            verify all gpu work units against a cpu gold standard\n" );
#else
  fprintf( stderr, " -d {lvl}   enable debugging info\n" );
  fprintf( stderr, " -h         this info\n" );
  fprintf( stderr, " -k {file}  specify a pot file (default: baracuda.pot)\n" );
  fprintf( stderr, " -s {file}  specify a previous session to continue\n" );
  fprintf( stderr, " -t {num}   specify number of threads (max: %d)\n", MAX_COMPARE_THREADS );
  fprintf( stderr, " -v         display version information\n" );
  fprintf( stderr, " -x         verify all gpu work unitx against a cpu gold standard\n" );
#endif

  fprintf( stderr, "\n" );
}

/* XXX need to build a probability based character pool builder */

/* constant character pools */
const char ptAlphaCharacters[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
const int ptAlphaCount = sizeof( ptAlphaCharacters );
const char ptAlphaNumCharacters[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0','1', '2', '3', '4', '5', '6','7', '8', '9' };
const int ptAlphaNumCount = sizeof( ptAlphaNumCharacters );
const char ptAllCharacters[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0','1', '2', '3', '4', '5', '6','7', '8', '9', '!', '@', '#' ,'$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', '[', '{', ']', '}', ';', ':', ' ', '\'', '\"', ',', '<', '.', '>', '/', '?', '`', '~' };
const int ptAllCount = sizeof( ptAllCharacters );

unsigned long loadBruteForceWorkUnits( workUnit_t *workUnits, unsigned long workUnitCount, int maxPwLen ) {
  unsigned long count = 0;
  int i, pos;
  char strBuf[MAX_PLAINTEXT_LEN];

  /* init on the first pass */
  if ( pbfState.ptCurLen EQ 0 )
    pbfState.ptCurLen = 1;

  while( count < workUnitCount ) {
    if ( pbfState.ptCurLen <= maxPwLen ) {
      /* add plaintext to workunits */
      workUnits[count].ptLen = pbfState.ptCurLen * 2;
      for ( i = 0; i < pbfState.ptCurLen; i++ ) {
	/* convert to UTF-16LE */
	workUnits[count].pt[(i*2)] = ptAllCharacters[pbfState.ptTokens[i]];
      }
      count++;
    } else {
      /* done */
      return count;
    }

    /* increment the plaintext */
    pbfState.ptTokens[0]++;

    /* check to see if any position rolls */
    for ( pos = 0; pos < pbfState.ptCurLen; pos++ ) {
      if ( pbfState.ptTokens[pos] >= ptAllCount ) {
	/* position rolled */
	pbfState.ptTokens[pos] = 0;
	if ( pos EQ pbfState.ptCurLen-1 ) {
	  printf( "%d len plaintexts exhausted\n", pbfState.ptCurLen );
	  pbfState.ptCurLen++;
	} else
	  pbfState.ptTokens[pos+1]++;
      }
    }
  }

  /* more to do */
  return count;
}

/****
 *
 * process work units (CPU)
 *
 ****/

unsigned long processWorkUnits( workUnit_t *workUnits, unsigned long workUnitCount, uint32_t hashSize ) {
  MD4_CTX ctx;
  uint32_t val;
  unsigned long count = 0;
  int i;

  while ( count < workUnitCount ) {
    /* generate the md4 hash */
    MD4_Init( &ctx );
    MD4_Update( &ctx, workUnits[count].pt, workUnits[count].ptLen );
    MD4_Final( workUnits[count].uHash, &ctx );
    workUnits[count].uhLen = NTLM_HASH_LEN;

    val = 0;
    /* generate the lookup hash */
    for( i = 0; i < workUnits[count].uhLen; i++ ) {
      int tmp;
      val = (val << 4) + ( workUnits[count].uHash[i] & 0xff );
      if ( tmp = (val & 0xf0000000)) {
	val = val ^ (tmp >> 24);
	val = val ^ tmp;
      }
    }
    workUnits[count].key = val % hashSize;

    count++;
  }

  return count;
}

/****
 *
 * compare thread wrapper
 *
 ****/

void threadCompareWorkUnits( void *threadId ) {
  long tId = (long)threadId;
  /* XXX should do 64bit compare if possible */
#ifdef X86_64
  uint64_t num_qwords;
  uint64_t *ua;
  uint64_t *ub;
  uint64_t *ua_max;
#else
  uint32_t num_dwords;
  uint32_t *ua;
  uint32_t *ub;
  uint32_t *ua_max;
#endif

  /* cleanup from last thread run */
  threadWorkUnits[tId].guessedCount = 0;
 
  /* search work units for matches */
  while( threadWorkUnits[tId].count < threadWorkUnits[tId].workUnitCount ) {
    threadWorkUnits[tId].done = FALSE;

    /* XXX moved inline to improve speed */
    threadWorkUnits[tId].hashRecPtr = threadWorkUnits[tId].uhHash->records[threadWorkUnits[tId].workUnits[threadWorkUnits[tId].count].key];
    while( threadWorkUnits[tId].hashRecPtr != NULL & ! threadWorkUnits[tId].done ) {
      /* use a fast inline dword compare */
#ifdef X86_64
      num_qwords = threadWorkUnits[tId].workUnits[threadWorkUnits[tId].count].uhLen / sizeof( uint64_t );
      ua = (uint64_t *)threadWorkUnits[tId].hashRecPtr->keyString;
      ub = (uint64_t *)threadWorkUnits[tId].workUnits[threadWorkUnits[tId].count].uHash;
      ua_max = ua + num_qwords;
#else
      num_dwords = threadWorkUnits[tId].workUnits[threadWorkUnits[tId].count].uhLen / sizeof( uint32_t );
      ua = (uint32_t *)threadWorkUnits[tId].hashRecPtr->keyString;
      ub = (uint32_t *)threadWorkUnits[tId].workUnits[threadWorkUnits[tId].count].uHash;
      ua_max = ua + num_dwords;
#endif

      while( ua < ua_max ) {
	if ( *ua EQ *ub ) {
	  ua++;
	  ub++;
	} else
	  break;
      }

      if ( ua EQ ua_max ) {
		  
	/****
	 *
	 * guessed one
	 *
	 ****/

	if ( threadWorkUnits[tId].hashRecPtr->data EQ NULL ) {
	  /* XXX should updatee hash with plaintext -> hash mapping */
	  threadWorkUnits[tId].guessedCount++;
	  saveGuessedHash( &threadWorkUnits[tId].workUnits[threadWorkUnits[tId].count] );

	  printf( "[%lu] Guessed (%s)\n",
		  currentTime - startTime,
		  utfConvert( threadWorkUnits[tId].workUnits[threadWorkUnits[tId].count].pt,
			      threadWorkUnits[tId].workUnits[threadWorkUnits[tId].count].ptLen,
			      threadWorkUnits[tId].tBuf, MAX_RBUF_LEN ) );
	}
	threadWorkUnits[tId].done = TRUE;
      } else
	threadWorkUnits[tId].hashRecPtr = threadWorkUnits[tId].hashRecPtr->next;
    }
      
    threadWorkUnits[tId].count++;
  }

  /*
   * lock
   */
  pthread_mutex_lock( &mutexWorkUnits );

  /* update total counts */
  totalGuessed += threadWorkUnits[tId].guessedCount;
  totalTries += threadWorkUnits[tId].count;

  /*
   * unlock
   */
  pthread_mutex_unlock ( &mutexWorkUnits );

  /* the thread is dead */
  pthread_exit(NULL);
}

/****
 *
 * SIGINT handler
 *
 ****/
 
void sigint_handler( int signo ) {
  signal( signo, SIG_IGN );

  /* do a calm shutdown as time permits */
  quit = save = TRUE;
  signal( signo, sigint_handler );
}

/****
 *
 * SIGTERM handler
 *
 ****/
 
void sigterm_handler( int signo ) {
  signal( signo, SIG_IGN );

  /* do a calm shutdown as time permits */
  quit = save = TRUE;
  signal( signo, sigterm_handler );
}

/****
 *
 * SIGHUP handler
 *
 ****/
 
void sighup_handler( int signo ) {
  signal( signo, SIG_IGN );

  /* time to rotate logs and check the config */
  reload = save = TRUE;
  signal( SIGHUP, sighup_handler );
}

/****
 *
 * SIGSEGV handler
 *
 ****/
 
void sigsegv_handler( int signo ) {
  signal( signo, SIG_IGN );

  fprintf( stderr, "Caught a sig%d, shutting down fast\n", signo );

  /* cuda wants a clean shutdown */
  if ( haveCuda )
    cudaExit();

  /* core out */
  abort();
}

/****
 *
 * SIGBUS handler
 *
 ****/
 
void sigbus_handler( int signo ) {
  signal( signo, SIG_IGN );
  
  fprintf( stderr, "Caught a sig%d, shutting down fast\n", signo );

  /* cuda wants a clean shutdown */
  if ( haveCuda )
    cudaExit();

  /* core out */
  abort();
}

/****
 *
 * SIGILL handler
 *
 ****/
 
void sigill_handler ( int signo ) {
  signal( signo, SIG_IGN );

  fprintf( stderr, "Caught a sig%d, shutting down fast\n", signo );

  /* cuda wants a clean shutdown */
  if ( haveCuda )
    cudaExit();

  /* core out */
  abort();
}

/****
 *
 * SIGFPE handler
 *
 ****/
 
void sigfpe_handler( int signo ) {
  signal( signo, SIG_IGN );

  fprintf( stderr, "Computer says no . . . sig%d, shutting down fast\n", signo );

  /* cuda wants a clean shutdown */
  if ( haveCuda )
    cudaExit();

  /* core out */
  abort();
}
